<?php
namespace Tbf\Bundle\DatabaseBundle\ThinkPhpModel;
use Tbf\Bundle\DatabaseBundle\Exception\DatabaseException;

/**
 * 对pdo的包装,
 * 简化一些常用操作,去掉很多不必要的细节
 * 1.query接受sql和数据,返回true或者关联数据或者抛出异常
 * 2.getPdo返回底层使用的pdo对象
 * */
class Tpdo
{
    protected $pdo;
    protected $debug;

    public function __construct($pdo)
    {
        $this->pdo = $pdo;
    }

    public function getPdo()
    {
        return $this->pdo;
    }

    /**
     * 接受2种输入
     * 1.sql
     *    使用普通的sql查询
     * 2.sql和data
     *    使用prepare查询data是prepare需要的输入数据
     * 有3种返回值:
     * 1.true
     *    表示sql执行成功,并且这条sql(理论上)没有返回值.
     * 2.[]map[string][string]
     *    表示sql执行成功,返回查询结果(可能没有数据会返回array())
     * 3.异常
     *    sql执行失败...要么sql有问题,要么数据库有问题..
     * */
    public function query($sql, $data = null, $debug = false)
    {
        if ($debug) {
            $this->debugQuery($sql, $data, $debug);
        }
        try {
            if (empty($data)) {
                return $this->querySimple($sql);
            }

            return $this->queryPrepare($sql, $data);
        } catch (\PDOException $e) {
            throw new DatabaseException('reason:' . $e->getMessage() . "\n" .
                    'sql:' . $sql . "\n"
            );
        }
    }

    protected function querySimple($sql)
    {
        $stat = $this->pdo->query($sql);

        return $this->handleReturn($sql, $stat);
    }

    protected function queryPrepare($sql, $data)
    {
        $stat = $this->pdo->prepare($sql);
        $stat->execute($data);

        return $this->handleReturn($sql, $stat);
    }

    protected function handleReturn($sql, $stat)
    {
        if ($this->isRead($sql)) {
            return $stat->fetchAll(\PDO::FETCH_ASSOC);
        } else {
            return true;
        }
    }

    /**
     * 获取一个字符串
     * 获取第一行第一个字段
     * 输入同query
     * 返回
     * 1.string 返回的数据
     * 2.true 执行成功,sql理论上没有返回值
     * 3.'' 没有返回数据
     */
    public function getOne($sql, $data = null, $debug = false)
    {
        $data = $this->query($sql, $data, $debug);
        if ($data === true) {
            return true;
        }
        if (empty($data)) {
            return '';
        }
        $data = array_shift($data);
        $data = array_shift($data);

        return $data;
    }

    /**
     * 获取一个行
     * 输入同query
     * 返回
     * 1.map[string]string 返回的数据
     * 2.true 执行成功,sql理论上没有返回值
     * 3.array() 没有返回数据
     */
    public function getRow($sql, $data = null, $debug = false)
    {
        $data = $this->query($sql, $data, $debug);
        if ($data === true) {
            return true;
        }
        if (empty($data)) {
            return array();
        }
        $data = array_shift($data);

        return $data;
    }

    /**
     * 返回pdo的lastInsertId
     * */
    public function lastInsertId()
    {
        return $this->pdo->lastInsertId();
    }

    /**
     * 这条sql在理论上是不是有返回值
     * */
    public function isRead($sql)
    {
        $sql = trim($sql);
        $actionPos = strpos($sql, ' ');
        $action = trim(strtolower(substr($sql, 0, $actionPos)));
        switch ($action) {
            case 'select':
            case 'show':
            case 'explain':
            case 'describe':
                return true;
            default:
        }

    }

    public function quoteTableName($table_name)
    {
        $ret = preg_match('/^[a-zA-Z0-9_.]*$/', $table_name);
        if (!$ret) {
            throw new DatabaseException(sprintf('invalid table name: %s', $table_name));
        }
        //中间会出现一个'.'
        $table_name = str_replace('.', '`.`', $table_name);

        return '`' . $table_name . '`';
    }

    public function quoteFieldName($field_name)
    {
        return $this->quoteTableName($field_name);
    }

    public function quote($value)
    {
        return $this->pdo->quote($value);
    }

    protected function debugQuery($sql, $data, $debug)
    {
        if ($debug === true) {
            $debug = 'print_r';
        }
        $debug($sql . "\n" . var_export($data, true));
    }
}
